#include <iostream>
#include <fstream>
#include <string>
#include <llvm/Support/CommandLine.h>
#include <llvm/Module.h>
#include <llvm/Support/IRReader.h>
#include <llvm/LLVMContext.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/System/Signals.h>
#include "constraint_parser.h"
#include "analyzer.h"

static llvm::cl::opt<std::string> bitcode_file("i", llvm::cl::desc("Input bitcode file to process"), llvm::cl::Required);
static llvm::cl::opt<std::string> result_file("o", llvm::cl::desc("Output parsed constraints"), llvm::cl::Required);
static llvm::cl::opt<std::string> algorithm("a", llvm::cl::desc("Algorithm to use"), llvm::cl::Required);

int main(int argc, char *argv[]){
	llvm::Module *module;
	llvm::SMDiagnostic diag;

	llvm::cl::ParseCommandLineOptions(argc, argv, 0);

	module = llvm::ParseIRFile(bitcode_file, diag, llvm::getGlobalContext());
	if(!module){
		std::cout<<"Failed to parse bitcode file."<<std::endl;
		return 1;
	}
	mypointsto::constraint_builder helper(module);
	helper.for_each_function();
	
	mypointsto::analyzer pointer_analyzer(&helper);
	//pointer_analyzer.initialize();
	//pointer_analyzer.dump_graph(std::cout);
	//pointer_analyzer.analyze(true);
	if(algorithm=="hybrid"){
		pointer_analyzer.initialize_hybrid();
		pointer_analyzer.analyze_hybrid();
	}else if(algorithm=="lazy"){
		pointer_analyzer.initialize();
		pointer_analyzer.analyze(true);
	}else if(algorithm=="generic"){
		pointer_analyzer.initialize();
		pointer_analyzer.analyze(false);
	}
	//pointer_analyzer.dump_graph(std::cout);
	
	std::ofstream fout;
	if(result_file!="cout"){
		fout.open(result_file.c_str());
		helper.dump_labels(fout);
		helper.dump_constraints(fout);
		pointer_analyzer.dump_analysis_result(fout);
	}else{
		helper.dump_labels(std::cout);
		helper.dump_constraints(std::cout);
		pointer_analyzer.dump_analysis_result(std::cout);
	}
	return 0;
}
